#!/usr/bin/env ruby


RedBook::Parser.flags = [:avg, :duration, :total, :count, :nodiff, :nobreak, :merge]

class RedBook::Logger

	# Initialize Tracker Plugin hooks. <i>[Tracker Plugin]</i>	
	def initialize_tracker
		@hooks.add :select_flag do |args|	tracker_average(args) end
		@hooks.add :select_flag do |args|	tracker_duration(args) end
		@hooks.add :select_flag do |args|	tracker_count(args) end
		@hooks.add :select_flag do |args|	tracker_total(args) end
	end
	
	protected
	
	# Track the average time spent on the completed activities 
	# in the current dataset. <i>[Tracker Plugin]</i>	
	def tracker_average(args)
		if args[:command][:avg] then
			count = 0
			total_time = 0
			activities = get_completed_activities
			activities = merge_activities(activities) if args[:command][:merge]
			activities.each do |a|
				total_time += a[:duration] #calculate_real_duration(a[:start], a[:finish])
			end
			begin
				element = {:info => "Average Duration"}
				element << {:duration => total_time/activities.length}
				element << {:total_activities => activities.length }
				notify(element)
			rescue
				notify({:warning => "No completed activities in current dataset."})
			end
			true
		else
			false
		end
	end
	
	# Track the duration of each completed activities 
	# in the current dataset. <i>[Tracker Plugin]</i>	
	def tracker_duration(args)
		if args[:command][:duration] then
			count = 0
			activities = get_completed_activities
			activities = merge_activities(activities) if args[:command][:merge]
			unless activities.blank? then
				activities.each do |a|
					count+=1
					element = a[:start].to_xhash_activity
					element << {:end_time => a[:finish][:timestamp]}
					element << {:duration => a[:duration]}#calculate_real_duration(a[:start], a[:finish])}
					notify(element, {:total => activities.length})
				end
			else
				notify({:warning => "No completed activities in current dataset."})
			end
			true
		else
			false
		end
	end

	# Counts the messages in the current dataset. <i>[Tracker Plugin]</i>	
	def tracker_count(args)
		if args[:command][:count] then
			element = {:total_activities => @dataset.length }
			notify(element)
			true
		else
			false
		end
	end
	
	# Track total time spent on the completed activities 
	# in the current dataset. <i>Tracker Plugin</i>
	def tracker_total(args)
		if args[:command][:total] then
			total_time = 0
			activities = get_completed_activities
			activities = merge_activities(activities) if args[:command][:merge]
			activities.each do |a|
				total_time += a[:duration] #calculate_real_duration(a[:start], a[:finish])
			end
			element = {:info => "Total Duration"}
			element << {:duration => total_time}
			element << {:total_activities => activities.length }
			notify(element)
			true
		else
			false
		end
	end
	
	private
	
	def merge_activities(activities) 
		activities.each do |a|
			activities.each do |b|
				if relogged_message?(a[:start], b[:start]) && relogged_message?(a[:finish], b[:finish]) && a != b then
					a[:finish] = b[:finish] # Update completed activity
					a[:duration] += b[:duration]
					activities.delete(b)
				end
			end
		end
		activities
	end
	
	def will_be_completed?(start_message)
		return false if completed?(start_message)
		start_index = find_message(start_message)
		index = start_index+1
		while (index < @log_frame.length) do
			if relogged_message?(start_message, @log_frame[index])
				return false
			else
				if matching_completed_activity?(start_message, @log_frame[index])
					return index
				end
			end
			index +=1
		end
		false
	end
	
	def get_total_duration(activities, limit)
		duration = 0
		upper_limit = activities[0][:end] unless activities.empty?
		compacted_activities = []
		activities_orig = activities.dup
		activities.each do |a|
			upper_limit = (a[:end] > limit) ? limit : a[:end]
			activities_orig.each do |at|
				unless is_sub_activity?(a, at) then
					if at[:start] < upper_limit && at[:start] > a[:start] then
						# a----at----/a---/at#
						if at[:end] > a[:end] then
							new_a = {:start => a[:end], :end => at[:end]}
							compacted_activities << new_a unless compacted_activities.include? new_a
							activities.delete(at)
						# a----/a----at---/at#
						else
							new_a= {:start => a[:start], :end => upper_limit}
							compacted_activities << new_a unless compacted_activities.include? new_a
						end
					else
						compacted_activities << a unless compacted_activities.include? a
					end
				else
					activities.delete(at)
				end
			end
		end
		compacted_activities.each do |c|
			duration += c[:end] - c[:start] unless (break?(find_by_timestamp(c[:start])) && !@command[:nobreak])
		end
		duration
	end
	
	def is_sub_activity? (a, sub_a)
		return (a[:start] < sub_a[:start] && a[:end] > sub_a[:end]) ? true : false
	end
	
	def calculate_real_duration(start_m, end_m)
		index = find_message(start_m)+1
		end_index = find_message(end_m)
		duration = end_m[:timestamp].to_i - start_m[:timestamp].to_i
		sub_activities = []
		while index < end_index do
			unless completed?(@log_frame[index])
				completed_activity = will_be_completed?(@log_frame[index])
				if completed_activity then
					sub_activities << {:start => @log_frame[index][:timestamp], :end => @log_frame[completed_activity][:timestamp]} unless @log_frame[completed_activity][:timestamp] > end_m[:timestamp] || concurrent?(@log_frame[index])
				end
			end
			index += 1
		end
		sub_duration = 0
		sub_duration = get_total_duration(sub_activities, end_m[:timestamp]) unless @command[:nodiff]
		duration - sub_duration
	end
	
	def get_completed_activities
		activities = []
		track_time do |start, finish|
			activities << {:start => start, :finish => finish}
		end
		activities.each do |a|
			activities.each do |b|
				if a[:finish][:timestamp] == b[:finish][:timestamp] && b[:start][:timestamp] > a[:start][:timestamp] then
					activities.delete(a)
				end
			end
		end
		# Set duration of each activity
		activities.each do |a|
			a[:duration] = calculate_real_duration(a[:start], a[:finish])
		end
		activities
	end
	
	def track_time(&block)
		# Find matching activities and track time
		count = 0
		@dataset.each do |m|
			if !completed?(m) then
				@dataset.each do |msg|
					if completed?(msg)
						if matching_completed_activity?(m, msg)
							yield(m, msg)
							break
						end
					end
				end
			end
		end
	end

end
